import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @Author : lizhifeng
 * @create 2022/8/13 17:40
 * @DESC 2022/8/13 17:40
 */


public class HeaderList {

    private static final int REWARD = 50 * 1000 * 1000;

    private List<Header> headerList = new ArrayList<>();

    private static HeaderList instance = new HeaderList();

    private HeaderList() {
    }

    public static HeaderList getInstance() {
        return instance;
    }


    public synchronized boolean addHeader(Header header) {
        boolean accept = accept(header);
        if (accept) {
            headerList.add(header);
        }
        return accept;
    }


    public synchronized boolean accept(Header header) {

        int size = headerList.size();
        if (size == 0) {
            return isFirst(header);
        }

        Header last = headerList.get(size - 1);
        byte[] ser = last.serialize();
        byte[] hash = Hash256.hash(Hash256.hash(ser));

        byte[] preHash = header.getPreHash();
        if (!Arrays.equals(hash, preHash)) {
            return false;
        }

        int lastTime = last.getTime();
        int time = header.getTime();
        if (lastTime > time) {
            return false;
        }


        int lastBits = last.getBits();
        byte[] serNew = header.serialize();
        byte[] hashNew = Hash256.hash(Hash256.hash(serNew));

        int nbit = Hash256.nbit(hashNew);

        int ave = average();
        if (ave < Constant.AVERAGE) {
            return (lastBits + 1) == nbit;
        } else if (ave > Constant.AVERAGE) {
            int expectBit = lastBits - 1;
            if (expectBit < Constant.MIN_BITS) {
                return lastBits == Constant.MIN_BITS;
            }
            return (lastBits - 1) == nbit;
        } else {
            return lastBits == nbit;
        }
    }


    public static boolean isFirst(Header header) {
        byte[] serNew = header.serialize();
        byte[] hashNew = Hash256.hash(Hash256.hash(serNew));
        BigInteger bigInteger = new BigInteger(hashNew);
        return bigInteger.equals(NetWork.FIRST);
    }


    public int average() {

        if (headerList.size() <= 1) {
            return 10;
        } else {
            Header first = headerList.get(0);
            int size = headerList.size();
            Header last = headerList.get(size - 1);
            int seconds = last.getTime() - first.getTime();
            return seconds / size / 60;
        }
    }


    public synchronized Header getLast() {
        int size = headerList.size();
        return headerList.get(size - 1);
    }


    private int getNextSubsidy() {
        if (headerList == null) {
            return REWARD;
        }
        return getSubsidy(headerList.size() + 1);
    }


    public static int getSubsidy(int height) {
        return REWARD >>> (height / 210000);
    }

    public static int getSubsidy() {
        return getInstance().getNextSubsidy();
    }

}
